Skip to content

[core] Resolve oneOf discriminator property type from child schemas#24172

Closed
Ignacio-Vidal wants to merge 1 commit into
OpenAPITools:masterfrom
Ignacio-Vidal:pr0-discriminator-type-from-children
Closed

[core] Resolve oneOf discriminator property type from child schemas#24172
Ignacio-Vidal wants to merge 1 commit into
OpenAPITools:masterfrom
Ignacio-Vidal:pr0-discriminator-type-from-children

Conversation

@Ignacio-Vidal

@Ignacio-Vidal Ignacio-Vidal commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What

getDiscriminatorPropertyType resolved the discriminator property's type only from the oneOf/anyOf schema's own properties, defaulting to "string" otherwise. For a pure oneOf/anyOf interface whose discriminator property lives on a shared base the children inherit via allOf, this produced a String getter that clashed with the enum the subtypes actually expose.

This adds a fallback (in DiscriminatorUtils) that resolves the type from the mapped child schemas, chasing the property through each child's allOf members. It only fires when the previous code would have returned "string", so existing specs are unaffected — regenerating the full sample suite produces no diffs.

Scenarios covered

Discriminator property declared… Resolves to Test
As a $ref on the schema's own properties (unchanged path) the referenced type (e.g. PetType) DiscriminatorUtilsTest, DefaultCodegenTest
Only on a shared base the oneOf children inherit via allOf PetType (from children) DiscriminatorUtilsTest, DefaultCodegenTest
Only on a shared base the anyOf children inherit via allOf PetType (from children) DiscriminatorUtilsTest
On a grandparent reached through multi-level allOf PetType (recursive descent) DiscriminatorUtilsTest
As a plain inline string (no $ref, no enum) "string" (fallback, unchanged) DiscriminatorUtilsTest, DefaultCodegenTest

Simples example where the discriminator is in the base object that is separate from the schema that contains oneOf

PetRequest:
      type: object
      oneOf:
        - $ref: '#/components/schemas/CatRequest'
        - $ref: '#/components/schemas/DogRequest'
      discriminator:
        propertyName: petType
        mapping:
          CAT: '#/components/schemas/CatRequest'
          DOG: '#/components/schemas/DogRequest'

    # Shared base WITHOUT a discriminator, so children flatten it via allOf
    # instead of extending it (which would create a cyclical reference).
    PetBase:
      type: object
      required:
        - petType
        - name
      properties:
        petType:
          $ref: '#/components/schemas/PetType'  # ---> This would be duplicated into PetRequest without this PR refactor
        name:
          type: string

    CatRequest:
      type: object
      allOf:
        - $ref: '#/components/schemas/PetBase'
        - type: object
          required:
            - indoor
          properties:
            indoor:
              type: boolean

    DogRequest:
      type: object
      allOf:
        - $ref: '#/components/schemas/PetBase'
        - type: object
          required:
            - trained
          properties:
            trained:
              type: boolean

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

Fixes discriminator type resolution for oneOf/anyOf interfaces by deriving the property type from child schemas (via allOf) with cycle-safe recursion. Getter types now use the correct enum (e.g., PetType) instead of "string", avoiding interface vs subtype mismatches.

  • Bug Fixes
    • Updated DiscriminatorUtils.getDiscriminatorPropertyType(OpenAPI, ...) to first check the schema’s own $ref, then fall back to oneOf/anyOf children via getDiscriminatorPropertyTypeFromChildren and recursive getDiscriminatorSchemaDeep (supports multi-level allOf with a visited guard for cycles).
    • Updated DefaultCodegen.getDiscriminatorPropertyType to pass openAPI, map to model names, and keep the "string" fallback.
    • Added DiscriminatorUtilsTest, expanded DefaultCodegenTest, and new YAMLs (oneOf/anyOf, nested base, cyclic, string fallback). Regenerating samples shows no diffs.

Written for commit a0934fe. Summary will update on new commits.

Review in cubic

@Ignacio-Vidal Ignacio-Vidal force-pushed the pr0-discriminator-type-from-children branch 2 times, most recently from 3a3cbe2 to 0a6c7f0 Compare June 30, 2026 20:59
@Ignacio-Vidal Ignacio-Vidal marked this pull request as ready for review June 30, 2026 21:16
@Ignacio-Vidal Ignacio-Vidal changed the title DRAFT: [core] Resolve oneOf discriminator property type from child schemas [core] Resolve oneOf discriminator property type from child schemas Jun 30, 2026
@Ignacio-Vidal

Ignacio-Vidal commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

@Mattias-Sehlstedt - This is an enhancement to your recent contribution 439f608 (introduce DiscriminatorUtils). Could you review it please?

@wing328 - Could you review i? Given you approved 439f608 too

@Ignacio-Vidal Ignacio-Vidal force-pushed the pr0-discriminator-type-from-children branch from 4532046 to 02c7380 Compare June 30, 2026 21:24

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found and verified against the latest diff

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@Ignacio-Vidal Ignacio-Vidal force-pushed the pr0-discriminator-type-from-children branch 2 times, most recently from 4483ce2 to 310c3f0 Compare June 30, 2026 22:22
@Mattias-Sehlstedt

Copy link
Copy Markdown
Contributor

Hi, see #24158 and #24156 for similar changes. For your change I would say that the same as in #24156 (comment) applies (i.e., is it really necessary to do two recursive passes?).

getDiscriminatorPropertyType resolved the discriminator property's type only
from the oneOf/anyOf schema's own properties, defaulting to "string" when the
property was not declared there. For a pure oneOf interface whose discriminator
property lives on a shared base that the children inherit via allOf, this
produced a String getter type that clashed with the enum the subtypes actually
expose.

Add the resolution to DiscriminatorUtils (alongside the existing
getDiscriminatorPropertyType / getDiscriminatorSchema): getDiscriminatorPropertyType
now falls back to the mapped child schemas, chasing the discriminator property
through each child's allOf members (getDiscriminatorPropertyTypeFromChildren /
getDiscriminatorSchemaDeep). The allOf descent tracks visited schemas to guard
against a cyclic allOf composition recursing infinitely. The fallback only fires
when the own-properties lookup is empty, so the type now resolves to the enum
ref (e.g. PetType) when a child declares the discriminator property as a $ref,
and otherwise still falls back to "string".

DefaultCodegen.getDiscriminatorPropertyType becomes a thin binding that maps the
resolved schema ref name to a generator-specific model name and applies the
"string" default, as those depend on this codegen's instance state.

The first resolution branch is unchanged, so existing specs are unaffected:
regenerating the full sample suite produces no diffs.
@Ignacio-Vidal Ignacio-Vidal force-pushed the pr0-discriminator-type-from-children branch from 310c3f0 to a0934fe Compare July 2, 2026 21:36
@Ignacio-Vidal

Copy link
Copy Markdown
Contributor Author

Hi, see #24158 and #24156 for similar changes. For your change I would say that the same as in #24156 (comment) applies (i.e., is it really necessary to do two recursive passes?).

I didn't see there were 2 more PRs addressing the same bug, happy to close mine in favour of #24158 and #24156

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants